#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include "hi_ext_util.h"
#include "hi_appcomm.h"
#include "plug_discovery.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* End of #ifdef __cplusplus */

typedef struct PLUG_DISCOVERY_CB_s
{
    PLUG_DISCOVERY_CB_EVENT pfnEventCB;
}PLUG_DISCOVERY_CB_S;

static HI_BOOL s_bDiscovryStart = HI_FALSE;
static pthread_t s_DiscoveryTaskId = 0;
static PLUG_DISCOVERY_CB_S s_pfnEventCB[PLUG_DISCOVERY_CB_MAXNUM] = { HI_NULL };

static HI_S32 PLUG_DISCOVERY_Event(PLUG_DISCOVERY_EVENT_S *pstEvent)
{
    HI_APPCOMM_CHECK_POINTER(pstEvent, HI_FALSE);
    HI_S32 s32Idx = 0;
    for(s32Idx = 0; s32Idx < PLUG_DISCOVERY_CB_MAXNUM; s32Idx++)
    {
        if (HI_NULL != s_pfnEventCB[s32Idx].pfnEventCB)
        {
            s_pfnEventCB[s32Idx].pfnEventCB(pstEvent);
        }
    }
    return HI_SUCCESS;
}

static HI_VOID* PLUG_DISCOVERY_Thread(HI_VOID)
{
    pthread_detach(pthread_self());
    HI_S32 s32AddrLength;
    HI_CHAR azMessage[1024 * 2];
    HI_S32 s32Optval = 1;
    HI_S32 s32Fd;
    struct sockaddr_in Addr;
    LOGI("PLUG_DISCOVERY_Thread started\n");
    if ((s32Fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        LOGE("socket fail\n");
        goto EXIT_ERROR;
    }

    if (setsockopt(s32Fd, SOL_SOCKET, SO_REUSEADDR, &s32Optval, sizeof(HI_S32)) < 0)
    {
        LOGE("setsockopt failed!\n");
    }
    memset(&Addr, 0, sizeof(struct sockaddr_in));
    Addr.sin_family = AF_INET;
    Addr.sin_addr.s_addr = INADDR_ANY;
    Addr.sin_port = htons(PLUG_DISCOVERY_PORT);
    s32AddrLength = sizeof(Addr);

    if (bind(s32Fd, (struct sockaddr *)&Addr, sizeof(Addr)) == -1)
    {
        LOGE("bind failed!\n");
    }

    while (1)
    {
        if (recvfrom(s32Fd, azMessage, sizeof(azMessage), 0, (struct sockaddr *)&Addr, &s32AddrLength) == -1)
        {
            LOGE("recv failed!\n");
        }
        //LOGI("recv:%s, from[%s]\n", azMessage, inet_ntoa(*((struct in_addr*)&(Addr.sin_addr))));
        PLUG_DISCOVERY_EVENT_S stEvent;
        stEvent.enType = PLUG_DISCOVERY_EVENT_NEWBROADCAST;
        snprintf(stEvent.stCfg.azIP, sizeof(stEvent.stCfg.azIP), "%s", inet_ntoa(*((struct in_addr*)&(Addr.sin_addr))));
        PLUG_DISCOVERY_Event(&stEvent);
    }

    close(s32Fd);

    return NULL;
EXIT_ERROR:
    s_bDiscovryStart = HI_FALSE;
    return NULL;
}

HI_S32 PLUG_DISCOVERY_Init(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_S32 s32Idx;

    for (s32Idx = 0; s32Idx < PLUG_DISCOVERY_CB_MAXNUM; s32Idx++)
    {
        s_pfnEventCB[s32Idx].pfnEventCB = HI_NULL;
    }

    s_bDiscovryStart = HI_TRUE;
    s32Ret = pthread_create(&s_DiscoveryTaskId, HI_NULL, PLUG_DISCOVERY_Thread, HI_NULL);
    if (HI_SUCCESS != s32Ret)
    {
        s_bDiscovryStart = HI_FALSE;
        LOGE("pthread create PLUG_DISCOVERY_Thread failed!\n");
    }

    return HI_SUCCESS;
}

HI_S32 PLUG_DISCOVERY_Deinit(HI_VOID)
{
    HI_S32 s32Idx;
    for (s32Idx = 0; s32Idx < PLUG_DISCOVERY_CB_MAXNUM; s32Idx++)
    {
        s_pfnEventCB[s32Idx].pfnEventCB = HI_NULL;
    }
    s_bDiscovryStart = HI_FALSE;

    return HI_SUCCESS;
}

/**
*   @brief  register discovery event callback
*   @param[in] pfnFaceDetectCB : PLUG_DISCOVERY_CB_EVENT
*   @retval  0 success,others failed
*/
HI_S32 PLUG_DISCOVERY_Register(PLUG_DISCOVERY_CB_EVENT *pfnEventCB)
{
    HI_APPCOMM_CHECK_POINTER(pfnEventCB, HI_FALSE);

    HI_S32 s32Idx;
    for (s32Idx = 0; s32Idx < PLUG_DISCOVERY_CB_MAXNUM; s32Idx++)
    {
        if (pfnEventCB == s_pfnEventCB[s32Idx].pfnEventCB)
        {
            LOGI("pfnEventCB is registed, return HI_SUCCESS\n");
            return HI_SUCCESS;
        }
        else if(HI_NULL == s_pfnEventCB[s32Idx].pfnEventCB)
        {
            s_pfnEventCB[s32Idx].pfnEventCB = pfnEventCB;
            return HI_SUCCESS;
        }
    }

    LOGE("s_pfnEventCB is fulled now, please unRegister first, return HI_FAILURE\n");
    return HI_FAILURE;
}

/**
*   @brief  unregister discovery event callback
*   @param[in] pfnFaceDetectCB : HI_FACEDETECT_CALLBACK
*   @retval  0 success,others failed
*/
HI_S32 PLUG_DISCOVERY_UnRegister(PLUG_DISCOVERY_CB_EVENT *pfnEventCB)
{
    HI_APPCOMM_CHECK_POINTER(pfnEventCB, HI_FALSE);

    HI_S32 s32Idx;
    for (s32Idx = 0; s32Idx < PLUG_DISCOVERY_CB_MAXNUM; s32Idx++)
    {
        if (pfnEventCB == s_pfnEventCB[s32Idx].pfnEventCB)
        {
            s_pfnEventCB[s32Idx].pfnEventCB = HI_NULL;
            return HI_SUCCESS;
        }
    }

    LOGE("can`t find pfnEventCB, return HI_FAILURE\n");
    return HI_FAILURE;

}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */

